--[[
  ObjectList class
  ----------------

  This class is designed to provide a better way to manage objects than
  the (now deprecated) resource_manager. The iterator provided is 
  particularly useful.

    Functions of ObjectList:
    ------------------------
      new(o)            --- constructor
      push_front(obj)   --- adds an object to the front of the list
      push_back(obj)    --- adds an object to the back of the list
      front()           --- returns the element at the front of the
                            list, else nil
      back()            --- returns the element at the back of the
                            list, else nil
      pop_front()       --- removes the front element from the list
      pop_back()        --- removes the back element from the list
      clear()           --- clears the list
      iterator()        --- returns an iterator for use in a generic
                            for loop
      update(delta)     --- updates all Object instances in the list, delta is optional
      draw()            --- draws all Object instances in the list
--]]


--object list node

ObjectListNode = {}
ObjectListNode.data = nil
ObjectListNode.next = nil

--constructor
function ObjectListNode:new(o)
  o = o or {}
  setmetatable(o, self)
  self.__index = self
  return o
end


--object list
ObjectList = {}
ObjectList.head = nil
ObjectList.tail = nil
ObjectList.temp = nil
ObjectList.size = 0

--constructor
function ObjectList:new(o)
  o = o or {}
  setmetatable(o, self)
  self.__index = self
  return o
end

--adds an object to the front of the list
function ObjectList:push_front(obj)
  if self.head == nil then
    self.head = ObjectListNode:new()
    self.tail = self.head
    self.head.data = obj
  else
    self.temp = ObjectListNode:new()
    self.temp.data = obj
    self.temp.next = self.head
    self.head = self.temp
    self.temp = nil
  end
  self.size = self.size + 1
end

--adds an object to the back of the list
function ObjectList:push_back(obj)
  if self.head == nil then
    self.head = ObjectListNode:new()
    self.tail = self.head
    self.head.data = obj
  else
    self.temp = ObjectListNode:new()
    self.temp.data = obj
    self.tail.next = self.temp
    self.tail = self.temp
    self.temp = nil
  end
  self.size = self.size + 1
end

--returns the first element in the list (so it can be used like a stack)
function ObjectList:front()
  if self.head ~= nil then
    return self.head.data
  else
    return nil
  end
end

--returns the last element in the list
function ObjectList:back()
  if self.head ~= nil then
    return self.tail.data
  else
    return nil
  end
end

--removes the first element
function ObjectList:pop_front()
  if self.head ~= nil then
    self.head = self.head.next
    self.size = self.size - 1
  end
end

--removes the last element
function ObjectList:pop_back()
  if self.head ~= nil then
    self.temp = self.head
    while self.temp.next.next ~= nil do
      self.temp = self.temp.next
    end
    self.tail = self.temp
    self.temp.next = nil
    self.size = self.size - 1
    self.temp = nil
  end
end

--clears the list
function ObjectList:clear()
  self.head = nil
  self.tail = nil
  self.temp = nil
  self.size = 0
end

--iterator for traversing the list
function ObjectList:iterator()
  local i, r
  return function ()
    if i == nil and r ~= true then
      i = ObjectListNode:new()
      i.next = self.head
      r = true
    end
    i = i.next
    if i then
      return i.data
    else
      return nil
    end
  end
end

--runs the update function of each Object contained within,
-- collecting any Objects that need to be collected
function ObjectList:update(delta)
  if self.head and self.head.data and self.head.data.collect == true then
  	self.head.data = nil
    self.head = self.head.next
    self.size = self.size - 1
  end

  self.temp = self.head
  while self.temp do
    if self.temp.next and self.temp.next.data and self.temp.next.data.collect == true then
      self.temp.next.data = nil
      self.temp.next = self.temp.next.next
      self.size = self.size - 1
    end
    if self.temp.data.update then self.temp.data:update(delta) end
    if self.temp then self.temp = self.temp.next end
  end
end

--calls draw on all of the Objects in the list
function ObjectList:draw()
  for obj in self:iterator() do
    if obj.draw then obj:draw() end
  end
end

--looks to see if an object exists already in the list
function ObjectList:exists(o)
  for obj in self:iterator() do
    if obj == o then return true end
  end
  return false
end
